package core.aln.pair;

import core.aln.Aligner;
import core.wm.Scoring;
import core.sb.SB;

/*Author: John Archer, Contact: john.archer.jpa@gmail.com*/
public class Paln extends Aligner{
    private double curH_M;
    private int curH_Mi;
    private int curH_Mj;

    public Paln(int gp, int ge, int mat, int mis){super(gp, ge, mat, mis);}

    public static int START_OF_REF_NOT_USED;
    public SB getAln(String tBit, String rBit, String qs) {
        Scoring[][] sc = getScoringMatrix(tBit, rBit);
        sc[tBit.substring(0, curH_Mi).length()][rBit.substring(0, curH_Mj).length()].set(true, false, false);
        StringBuffer [] aln = getAln(tBit.substring(0, curH_Mi), rBit.substring(0, curH_Mj), qs.substring(0, curH_Mj), sc);
        String [] alnArr = {aln[0].toString(), aln[1].toString(), aln[2].toString()};
        SB rslt = new SB(alnArr);
        START_OF_REF_NOT_USED = curH_Mi - (rslt.get(0).getDNA().length() - countGaps(rslt.get(0).getDNA()));
        return rslt;
    }

    private int countGaps(String str){
        int c = 0;
        for(int x=0; x<str.length();x++){if(str.charAt(x)=='-'){c++;} }
        return c;
    }

    private StringBuffer [] getAln(String s1, String s2, String q, Scoring[][] sc){
        int lnS1 = s1.length();int lnS2 = s2.length();
        int lnS1m1 = lnS1 - 1;int lnS2m1 = lnS2 - 1;
        StringBuffer [] al = {new StringBuffer(""), new StringBuffer(""), new StringBuffer("")};
        if (lnS1 == 0) {
            for (int x = 0; x < lnS2; x++){al[0].append(gap);}
            al[1].append(s2);al[2].append(q);
            return al;
        }
        if (lnS2 == 0) {
            al[0].append(s1);
            for (int x = 0; x < lnS1; x++) {al[1].append(gap);al[2].append(gap);}
            return al;
        }

        double pPar = pPara(s1.charAt(lnS1m1), s2.charAt(lnS2m1));
        if (sc[lnS1][lnS2].getVal() == 0.00 && sc[lnS1][lnS2].mMode) {
            if (lnS1 < lnS2) {
                for (int x = 0; x < lnS2; x++){al[0].append(gap);}
                al[1].append(s2);al[2].append(q);
            } else {
                al[0].append(s1);
                for (int x = 0; x < lnS1; x++){al[1].append(gap);al[2].append(gap);}
            }
            return al;
        }
        else if (sc[lnS1][lnS2].getVal() == sc[lnS1m1][lnS2].m + go) {
            sc[lnS1m1][lnS2].set(true, false, false);
            al = getAln(s1.substring(0, lnS1m1), s2, q, sc);
            al[0].append(s1.charAt(lnS1m1));
            al[1].append(gap);al[2].append(gap);
        } else if (sc[lnS1][lnS2].getVal() == sc[lnS1m1][lnS2].ix + ge) {
            sc[lnS1m1][lnS2].set(false, true, false);
            al = getAln(s1.substring(0, lnS1m1), s2, q, sc);
            al[0].append(s1.charAt(lnS1m1));
            al[1].append(gap);al[2].append(gap);
        }
        else if (sc[lnS1][lnS2].getVal() == sc[lnS1][lnS2m1].m + go) {
            sc[lnS1][lnS2m1].set(true, false, false);
            al = getAln(s1, s2.substring(0, lnS2m1), q.substring(0, lnS2m1), sc);
            al[0].append(gap);
            al[1].append(s2.charAt(lnS2m1));
            al[2].append(q.charAt(lnS2m1));
        } else if (sc[lnS1][lnS2].getVal() == sc[lnS1][lnS2m1].iy + ge) {
            sc[lnS1][lnS2m1].set(false, false, true);
            al = getAln(s1, s2.substring(0, lnS2m1), q.substring(0, lnS2m1), sc);
            al[0].append(gap);
            al[1].append(s2.charAt(lnS2m1));
            al[2].append(q.charAt(lnS2m1));
        } else if (sc[lnS1][lnS2].getVal() == (sc[lnS1m1][lnS2m1].m + pPar)) {
            sc[lnS1m1][lnS2m1].set(true, false, false);
            al = getAln(s1.substring(0, lnS1m1), s2.substring(0, lnS2m1), q.substring(0, lnS2m1), sc);
            al[0].append(s1.charAt(lnS1m1));
            al[1].append(s2.charAt(lnS2m1));
            al[2].append(q.charAt(lnS2m1));
        } else if (sc[lnS1][lnS2].getVal() == (sc[lnS1m1][lnS2m1].ix + pPar)) {
            sc[lnS1m1][lnS2m1].set(false, true, false);
            al = getAln(s1.substring(0, lnS1m1), s2.substring(0, lnS2m1), q.substring(0, lnS2m1), sc);
            al[0].append(s1.charAt(lnS1m1));
            al[1].append(s2.charAt(lnS2m1));
            al[2].append(q.charAt(lnS2m1));
        } else if (sc[lnS1][lnS2].getVal() == (sc[lnS1m1][lnS2m1].iy + pPar)) {
            sc[lnS1m1][lnS2m1].set(false, false, true);
            al = getAln(s1.substring(0, lnS1m1), s2.substring(0, lnS2m1), q.substring(0, lnS2m1), sc);
            al[0].append(s1.charAt(lnS1m1));
            al[1].append(s2.charAt(lnS2m1));
            al[2].append(q.charAt(lnS2m1));
        }
        return al;
    }

    private Scoring [][] getScoringMatrix(String s1, String s2) {
        int lnS1 = s1.length();int lnS2 = s2.length();
        int lnS1p1 = lnS1 + 1;int lnS2p1 = lnS2 + 1;
        Scoring[][] scores = new Scoring[lnS1p1][lnS2p1];
        for (int i = 0; i < lnS1p1; i++) {scores[i][0] = new Scoring(0.00, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);}
        for (int j = 0; j < lnS2p1; j++){scores[0][j] = new Scoring(0.00, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);}
        scores[0][0] = new Scoring(0.00, 0.00, 0.00);
        curH_M = Double.NEGATIVE_INFINITY;curH_Mi = 0;curH_Mj = 0;
        double curM = 0;double pPar;double[] M_val = new double[4];
        double ix1, ix2, iy1, iy2;


        int imin1 = 0;int jmin1 = 0;
        for (int i = 1; i < lnS1p1; i++) {
            imin1 = i - 1;
            for (int j = 1; j < lnS2p1; j++) {
                jmin1 = j - 1;
                pPar = pPara(s1.charAt(imin1), s2.charAt(jmin1));
                M_val[0] = scores[imin1][jmin1].m + pPar;
                M_val[1] = scores[imin1][jmin1].ix + pPar;
                M_val[2] = scores[imin1][jmin1].iy + pPar;
                M_val[3] = 0;
                ix1 = scores[imin1][j].m + go;
                ix2 = scores[imin1][j].ix + ge;
                iy1 = scores[i][jmin1].m + go;
                iy2 = scores[i][jmin1].iy + ge;
                curM = maxVal(M_val);
                scores[i][j] = new Scoring(curM,(ix1>ix2)?ix1:ix2,(iy1>iy2)?iy1:iy2);
                if (curM > curH_M){curH_M = curM;curH_Mi = i;curH_Mj = j;}
            }
        }
        return scores;
    }
}